package app.itgungnir.compose.wan.widgets

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.rememberImagePainter
import coil.size.Scale
import com.google.accompanist.pager.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlin.math.absoluteValue

data class BannerItem(
    val coverUrl: String? = null,
    val linkUrl: String? = null
)

@OptIn(ExperimentalPagerApi::class)
@Composable
fun WanBanner(
    modifier: Modifier = Modifier,
    itemsState: State<List<BannerItem>?>,
    visibleState: State<Boolean>,
    onItemClick: (BannerItem) -> Unit = {}
) {
    val items = itemsState.value ?: return
    val itemCount = items.size
    if (itemCount <= 0) {
        return
    }
    val viewModel: WanBannerViewModel = viewModel(factory = WanBannerViewModelFactory(itemCount))
    val pagerState = rememberPagerState(initialPage = itemCount)
    val bannerChangeState = viewModel.bannerIndexState.collectAsState()
    LaunchedEffect(key1 = pagerState) {
        snapshotFlow { pagerState.currentPage }.collect { index ->
            val pageCount = pagerState.pageCount / 3
            if ((index < pageCount || index >= pageCount * 2) && pageCount > 0) {
                val newIndex = index % pageCount + pageCount
                pagerState.scrollToPage(newIndex)
            }
        }
    }
    LaunchedEffect(key1 = pagerState) {
        snapshotFlow { pagerState.currentPageOffset }.collect { offset ->
            if (pagerState.pageCount > 0) {
                when (offset) {
                    0F -> viewModel.startCountDown(pagerState.currentPage)
                    else -> viewModel.stopCountDown()
                }
            }
        }
    }
    LaunchedEffect(key1 = bannerChangeState.value) {
        if (pagerState.pageCount > 0) {
            val nextIndex = bannerChangeState.value + itemCount
            pagerState.animateScrollToPage(nextIndex)
        }
    }
    LaunchedEffect(key1 = visibleState.value) {
        if (pagerState.pageCount > 0) {
            when (visibleState.value) {
                true -> viewModel.startCountDown(pagerState.currentPage)
                else -> viewModel.stopCountDown()
            }
        } else {
            viewModel.stopCountDown()
        }
    }
    Box(modifier = modifier, contentAlignment = Alignment.BottomCenter) {
        HorizontalPager(
            count = itemCount * 3,
            state = pagerState,
            contentPadding = PaddingValues(horizontal = 35.dp)
        ) { index ->
            val item = items.getOrNull(index % itemCount) ?: return@HorizontalPager
            WanBannerItem(this, index, item) {
                onItemClick.invoke(it)
            }
        }
        WanBannerIndicator(modifier = Modifier.padding(bottom = 10.dp), pagerState = pagerState)
    }
}

@OptIn(ExperimentalPagerApi::class, ExperimentalMaterialApi::class)
@Composable
private fun WanBannerItem(pagerScope: PagerScope, index: Int, item: BannerItem, onItemClick: (BannerItem) -> Unit) {
    Card(
        modifier = Modifier
            .graphicsLayer {
                val pageOffset = pagerScope.calculateCurrentOffsetForPage(index).absoluteValue / 3F
                lerp(start = 0.7F, stop = 1F, fraction = 1F - pageOffset.coerceIn(0F, 1F)).also { scale ->
                    scaleX = scale
                    scaleY = scale
                }
                alpha = lerp(start = 0.5f, stop = 1f, fraction = 1f - pageOffset.coerceIn(0f, 1f))
            }
            .fillMaxWidth()
            .aspectRatio(1.77F),
        shape = RoundedCornerShape(12.dp),
        onClick = { onItemClick.invoke(item) }
    ) {
        Image(
            modifier = Modifier.fillMaxWidth(),
            painter = rememberImagePainter(data = item.coverUrl) {
                this.scale(Scale.FILL)
            },
            contentDescription = ""
        )
    }
}

@OptIn(ExperimentalPagerApi::class)
@Composable
private fun WanBannerIndicator(modifier: Modifier = Modifier, pagerState: PagerState) {
    val pageCount = pagerState.pageCount / 3
    if (pageCount <= 0) {
        return
    }
    val indicatorWidthPx = LocalDensity.current.run { 8.dp.roundToPx() }
    val spacingPx = LocalDensity.current.run { 8.dp.roundToPx() }
    val currentIndicatorPositionState = remember { mutableStateOf(0) }
    Box(modifier = modifier, contentAlignment = Alignment.CenterStart) {
        Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
            val indicatorModifier = Modifier
                .size(width = 8.dp, height = 8.dp)
                .background(color = Color.LightGray, shape = CircleShape)
            repeat(pageCount) {
                Box(indicatorModifier)
            }
        }
        LaunchedEffect(key1 = pagerState) {
            snapshotFlow { pagerState.currentPage % pageCount + pagerState.currentPageOffset }.collect { offset ->
                if (pagerState.pageCount > 0) {
                    val scrollPosition = offset.coerceIn(0F, (pageCount - 1).toFloat())
                    currentIndicatorPositionState.value = ((spacingPx + indicatorWidthPx) * scrollPosition).toInt()
                }
            }
        }
        Box(
            Modifier
                .offset { IntOffset(x = currentIndicatorPositionState.value, y = 0) }
                .size(width = 8.dp, height = 8.dp)
                .background(color = MaterialTheme.colors.primary, shape = CircleShape)
        )
    }
}

@Suppress("UNCHECKED_CAST")
class WanBannerViewModelFactory(private val pageCount: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return when (modelClass.isAssignableFrom(WanBannerViewModel::class.java)) {
            true -> WanBannerViewModel(pageCount) as T
            else -> throw IllegalArgumentException("Unknown ViewModel class.")
        }
    }
}

class WanBannerViewModel(private val pageCount: Int) : ViewModel() {

    val bannerIndexState = MutableStateFlow(0)

    private var countDownJob: Job? = null

    fun startCountDown(currentPageIndex: Int) {
        stopCountDown()
        if (pageCount <= 0) {
            return
        }
        countDownJob = viewModelScope.launch {
            delay(5000L)
            val currentIndex = currentPageIndex % pageCount
            bannerIndexState.value = when (currentIndex + 1 > pageCount) {
                true -> 1
                else -> currentIndex + 1
            }
            startCountDown(bannerIndexState.value)
        }
    }

    fun stopCountDown() {
        countDownJob?.cancel()
    }
}
